package com.hzw.code.vm

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.hzw.code.net.IResource
import com.hzw.code.net.exception.ResourceException
import com.hzw.code.utils.convertException
import com.hzw.code.vm.event.CommonEvent
import com.hzw.code.vm.event.Event
import com.hzw.code.vm.event.ICommonEvent
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

/**
 *
 * @Author:         HuaZhongWei
 * @CreateDate:     2020/4/18 9:42 PM
 *
 * @Description:    APP的顶层ViewModel
 *
 */
open class AbsViewModel : ViewModel(), ICommonEvent {

    private val _eventLiveData by lazy {
        MutableLiveData<CommonEvent>()
    }

    val eventLiveData: LiveData<CommonEvent> = _eventLiveData

    fun launchUI(block: suspend CoroutineScope.() -> Unit) = viewModelScope.launch { block() }

    fun <T> launchFlow(block: suspend () -> T): Flow<T>  = flow { emit(block()) }


    /**
     * 统一处理异常
     * @param block 可能会发生异常的代码块
     * @param error 异常处理方式
     * @param complete 无论异常与否，都必须执行的
     */
    suspend fun handleException(
        block: suspend CoroutineScope.() -> Unit,
        error: suspend CoroutineScope.(ResourceException) -> Unit,
        complete: suspend CoroutineScope.() -> Unit
    ) {
        coroutineScope {
            try {
                block()
            } catch (e: Exception) {
                error(convertException(e))
            } finally {
                complete()
            }
        }
    }

    /**
     * 统一处理异常
     * @param block 请求数据代码块
     * @param success 过滤只会成功的
     * @param error 异常处理方式
     * @param complete 无论异常与否，都必须执行的
     */
    suspend fun <T> handleException(
        block: suspend CoroutineScope.() -> IResource<T>,
        success: suspend CoroutineScope.(IResource<T>) -> Unit,
        error: suspend CoroutineScope.(ResourceException) -> Unit,
        complete: suspend CoroutineScope.() -> Unit
    ) {
        coroutineScope {
            try {
                success(block())
            } catch (e: Exception) {
                error(convertException(e))
            } finally {
                complete()
            }
        }
    }

    /**
     * 过滤请求
     * 不成功则抛出异常
     * @param resource 请求
     * @param success 成功的数据处理
     */
    suspend fun <T> handleResource(
        resource: IResource<T>,
        success: suspend CoroutineScope.(T) -> Unit
    ) {
        coroutineScope {
            if (resource.isSuccess()) {
                success(resource.data())
            } else{
                throw ResourceException(resource)
            }
        }
    }

    /**
     * 统一处理处理错误的执行流程，但是不包含请求不成功的处理
     */
    fun launch(
        block: suspend CoroutineScope.() -> Unit,
        error: suspend CoroutineScope.(ResourceException) -> Unit,
        complete: suspend CoroutineScope.() -> Unit,
        isShowLoading: Boolean = true
    ) {
        if (isShowLoading) {
            showLoading()
        }
        launchUI {
            handleException(
                block = {
                    withContext(Dispatchers.IO) {
                        block()
                    }
                },
                error = {
                    error(it)
                },
                complete = {
                    if (isShowLoading) {
                        hideLoading()
                    }
                    complete()
                }
            )
        }
    }


    /**
     * 统一处理处理错误的执行流程，只有成功处理
     */
    fun <T> launchOnlySuccess(
        block: suspend CoroutineScope.() -> IResource<T>,
        success: (T) -> Unit,
        error: suspend CoroutineScope.(ResourceException) -> Unit,
        complete: suspend CoroutineScope.() -> Unit,
        isShowLoading: Boolean = true
    ) {
        if (isShowLoading) {
            showLoading()
        }
        launchUI {
            handleException(
                block = {
                    withContext(Dispatchers.IO) {
                        block()
                    }
                },

                success = { resource ->
                    handleResource(resource) {
                        success(it)
                    }
                },

                error = {
                    error(it)
                },

                complete = {
                    if (isShowLoading) {
                        hideLoading()
                    }
                    complete()
                }
            )
        }
    }

    override fun showLoading() {
        _eventLiveData.value = CommonEvent(Event.SHOW_LOADING)
    }

    override fun hideLoading() {
        _eventLiveData.value = CommonEvent(Event.HIDE_LOADING)
    }

    override fun showContent() {
        _eventLiveData.value = CommonEvent(Event.CONTENT)
    }

    override fun showError() {
        _eventLiveData.value = CommonEvent(Event.ERROR)
    }

    override fun showNetError() {
        _eventLiveData.value = CommonEvent(Event.NET_ERROR)
    }

    override fun showEmpty() {
        _eventLiveData.value = CommonEvent(Event.EMPTY)
    }

    override fun showToast(msg: String?) {
        _eventLiveData.value = CommonEvent(Event.TOAST, msg)
    }
}